package com.zhs.tac;

import java.util.ArrayList;

import com.zhs.javacc.SimpleNode;
import com.zhs.type.Method;
import com.zhs.type.Type;

public class TACGeneration {
	private ArrayList<String> tacs;
	private int tempCounter;
	private int labelCounter;
	StringBuffer result;

	public void gen(SimpleNode node) {
		if (node.toString().equals("Statement")) {
			// go on with nesting sub-statements
			gen((SimpleNode) node.jjtGetChild(0));
		} else if (node.toString().equals("Block")) {
			for (int i = 0; i < node.jjtGetNumChildren(); i++) {
				// iterate all nesting statements
				gen((SimpleNode) node.jjtGetChild(i));
			}
		} else if (node.toString().equals("IfStatement")) {
			// if (exp) statement else statement
			// check node.jjtgetchild(0) for if conditions
			// condition = child 0,
			// nesting statement = child 1;
			// if xxoo goto L0
			// goto L1
			// L0:
			// statement 1
			// goto L2
			// L1:
			// statement 2
			// L2:
			// next label will start from L3
			genExp((SimpleNode) node.jjtGetChild(0));
			int lastTemp = tempCounter - 1;
			int currentLabel = labelCounter;
			labelCounter += 3;
			tacs.add("if t" + lastTemp + " goto L" + currentLabel);
			tacs.add("goto L" + (currentLabel + 1));
			tacs.add("L" + currentLabel + ":");
			gen((SimpleNode) node.jjtGetChild(1));
			tacs.add("goto L" + (currentLabel + 2));
			tacs.add("L" + (currentLabel + 1) + ":");
			gen((SimpleNode) node.jjtGetChild(2));
			tacs.add("L" + (currentLabel + 2) + ":");
		} else if (node.toString().equals("WhileStatement")) {
			// while (exp) statement
			// check child 0 for condition
			// nesting statement = child 1
			// L0:
			// if xxoo goto L1
			// goto L2
			// L1:
			// statement
			// goto L0
			// L2:
			tacs.add("L" + labelCounter + ":");
			genExp((SimpleNode) node.jjtGetChild(0));
			int lastTemp = tempCounter - 1;
			int currentLabel = labelCounter;
			labelCounter += 3;
			tacs.add("if t" + lastTemp + " goto L" + (currentLabel + 1));
			tacs.add("goto L" + (currentLabel + 2));
			tacs.add("L" + (currentLabel + 1) + ":");
			gen((SimpleNode) node.jjtGetChild(1));
			tacs.add("goto L" + currentLabel);
			tacs.add("L" + (currentLabel + 2) + ":");
		} else if (node.toString().equals("DoWhileStatement")) {
			// do statement while(exp);
			tacs.add("L" + labelCounter + ":");
			int currentLabel = labelCounter;
			labelCounter++;
			gen((SimpleNode) node.jjtGetChild(0));
			genExp((SimpleNode) node.jjtGetChild(1));
			int lastTemp = tempCounter - 1;
			tacs.add("if t" + lastTemp + " goto L" + (currentLabel));
		} else if (node.toString().equals("AssignmentStatement")) {
			// id = exp;
			// var = child 0, exp = child 1;
			genExp((SimpleNode) node.jjtGetChild(1));
			tacs
					.add(((SimpleNode) node.jjtGetChild(0)).jjtGetFirstToken().image
							+ " := t" + (tempCounter - 1));
		} else if (node.toString().equals("ArrayAssignmentStatement")) {
			// id[exp] = exp;
			// id child 0, offset child 1, exp child 2;
			genExp((SimpleNode) node.jjtGetChild(1));
			String temp = "t" + (tempCounter - 1);
			genExp((SimpleNode) node.jjtGetChild(2));
			tacs
					.add(((SimpleNode) node.jjtGetChild(0)).jjtGetFirstToken().image
							+ "[" + temp + "] = t" + (tempCounter - 1));
		} else if (node.toString().equals("PrintStatement")) {
			// sout(exp);
			// seems ok to accept any parameter....
			// exp child 0;
			genExp((SimpleNode) node.jjtGetChild(0));
			tacs.add("param t" + (tempCounter - 1));
			tacs.add("call System.out.println,1");
		}
	}

	public void genExp(SimpleNode node) {
		String current_exp = node.toString();
		if (current_exp.equals("And")) {
			genAnd(node);
		} else if (current_exp.equals("Compare")) {
			genCompare(node);
		} else if (current_exp.equals("Add")) {
			genAdd(node);
		} else if (current_exp.equals("Sub")) {
			genSub(node);
		} else if (current_exp.equals("Times")) {
			genTimes(node);
		} else if (current_exp.equals("ArrayLengthExpression")) {
			genLength(node);
		} else if (current_exp.equals("AllocationIntExpression")) {
			genIntArray(node);
		} else if (current_exp.equals("AllocationDoubleExpression")) {
			genDoubleArray(node);
		} else if (current_exp.equals("AllocationIDExpression")) {
			genIDAllocation(node);
		} else if (current_exp.equals("MethodInvokeExpression")) {
			genMethodInvoke(node);
		} else if (current_exp.equals("ArrayAccessExpression")) {
			genArrayAccess(node);
		} else if (current_exp.equals("ParenthesisExpression")) {
			genParenthesis(node);
		} else if (current_exp.equals("NoExpression")) {
			genNo(node);
		} else if (current_exp.equals("IntegerLiteral")
				|| current_exp.equals("DoubleLiteral")
				|| current_exp.equals("TrueLiteral")
				|| current_exp.equals("FalseLiteral")
				|| current_exp.equals("Identifier")
				|| current_exp.equals("ThisExpression")) {
			tacs
					.add("t" + tempCounter + " := "
							+ node.jjtGetFirstToken().image);
			tempCounter++;
		}
	}

	private void genNo(SimpleNode node) {
		genExp((SimpleNode) node.jjtGetChild(0));
		tacs.add("t" + tempCounter + " := !t" + (tempCounter - 1));
		tempCounter++;
	}

	private void genParenthesis(SimpleNode node) {
		genExp((SimpleNode) node.jjtGetChild(0));
	}

	private void genArrayAccess(SimpleNode node) {
		genExp((SimpleNode) node.jjtGetChild(1).jjtGetChild(0));
		tacs.add("t" + tempCounter + " := "
				+ ((SimpleNode) node.jjtGetChild(0)).jjtGetFirstToken().image
				+ "[t" + (tempCounter - 1) + "]");
		tempCounter++;
	}

	private void genMethodInvoke(SimpleNode node) {
		SimpleNode parameters = (SimpleNode) node.jjtGetChild(1).jjtGetChild(1);
		ArrayList<Integer> list = new ArrayList<Integer>();
		for (int i = 0; i < parameters.jjtGetNumChildren(); i++) {
			genExp((SimpleNode) parameters.jjtGetChild(i));
			list.add(tempCounter - 1);
		}
		for (int i = 0; i < parameters.jjtGetNumChildren(); i++) {
			tacs.add("param t" + list.get(i));
		}
		tacs.add("call "
				+ ((SimpleNode) (node.jjtGetChild(1).jjtGetChild(0)))
						.jjtGetFirstToken().image + ", "
				+ parameters.jjtGetNumChildren());
		tacs.add("pop t" + tempCounter);
		tempCounter++;
	}

	private void genIDAllocation(SimpleNode node) {
		tacs.add("t" + tempCounter + " := " + "new "
				+ ((SimpleNode) node.jjtGetChild(0)).jjtGetFirstToken().image
				+ "()");
		tempCounter++;
	}

	private void genDoubleArray(SimpleNode node) {
		genExp((SimpleNode) node.jjtGetChild(0));
		tacs.add("t" + tempCounter + " := " + "new double[t"
				+ (tempCounter - 1) + "]");
		tempCounter++;
	}

	private void genIntArray(SimpleNode node) {
		genExp((SimpleNode) node.jjtGetChild(0));
		tacs.add("t" + tempCounter + " := " + "new int[t" + (tempCounter - 1)
				+ "]");
		tempCounter++;
	}

	private void genLength(SimpleNode node) {
		tacs.add("t" + tempCounter + " := "
				+ ((SimpleNode) node.jjtGetChild(0)).jjtGetFirstToken().image
				+ ".length");
		tempCounter++;
	}

	private void genTimes(SimpleNode node) {
		int lastTemp = -1;
		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
			genExp((SimpleNode) node.jjtGetChild(i));
			if (i > 0) {
				// lastTemp && tempcounter-1
				tacs.add("t" + tempCounter + " := " + "t " + lastTemp + " * t"
						+ (tempCounter - 1));
				lastTemp = tempCounter - 1;
				tempCounter++;
			} else {
				lastTemp = tempCounter - 1;
			}
		}
	}

	private void genSub(SimpleNode node) {
		int lastTemp = -1;
		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
			genExp((SimpleNode) node.jjtGetChild(i));
			if (i > 0) {
				// lastTemp && tempcounter-1
				tacs.add("t" + tempCounter + " := " + "t " + lastTemp + " - t"
						+ (tempCounter - 1));
				lastTemp = tempCounter - 1;
				tempCounter++;
			} else {
				lastTemp = tempCounter - 1;
			}
		}
	}

	private void genAdd(SimpleNode node) {
		int lastTemp = -1;
		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
			genExp((SimpleNode) node.jjtGetChild(i));
			if (i > 0) {
				// lastTemp && tempcounter-1
				tacs.add("t" + tempCounter + " := " + "t" + lastTemp + " + t"
						+ (tempCounter - 1));
				lastTemp = tempCounter - 1;
				tempCounter++;
			} else {
				lastTemp = tempCounter - 1;
			}
		}
	}

	private void genCompare(SimpleNode node) {
		genExp((SimpleNode) node.jjtGetChild(0));
		int tempLeft = tempCounter - 1;
		genExp((SimpleNode) node.jjtGetChild(1));
		tacs.add("t" + tempCounter + " := t" + tempLeft + " < t"
				+ (tempCounter - 1));
		tempCounter++;
	}

	private void genAnd(SimpleNode node) {
		int lastTemp = -1;
		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
			genExp((SimpleNode) node.jjtGetChild(i));
			if (i > 0) {
				// lastTemp && tempcounter-1
				tacs.add("t" + tempCounter + " := " + "t " + lastTemp + " && t"
						+ (tempCounter - 1));
				lastTemp = tempCounter - 1;
				tempCounter++;
			} else {
				lastTemp = tempCounter - 1;
			}
		}
	}

	public String getResult() {
		return result.toString();
	}

	public TACGeneration() {
		labelCounter = 0;
		tempCounter = 0;
		tacs = new ArrayList<String>();
		for (Type type : Type.getAllTypes())
			for (Method method : type.getMethods()) {
				tacs.add(method.getMethodName() + ":");
				tempCounter = 0;
				for (SimpleNode node : method.getStatementNodes()) {
					gen(node);
				}
				SimpleNode returnNode = method.getReturnNode();
				if (returnNode != null) {
					genExp((SimpleNode) returnNode.jjtGetChild(0));
					tacs.add("push t" + (tempCounter - 1));
				}
				tacs.add("");
			}
		result = new StringBuffer(1000);
		for (String s : tacs) {
			result.append(s);
			result.append('\n');
		}
		System.out.println(result);
	}
}
